//哈夫曼编码与解码
#define ENCODE(buf, size, offset, data)\
     if ((NULL == buf) || ( 0== size))\
     {\
     return -1;\
     }\
     if (offset + sizeof(data) > size)\
     {\
     return -1;\
     }\
     {\
     uint8_t *p = (uint8_t*)buf;\
     p=p+offset; \
     memcpy(p,&data,sizeof(data)); \
     offset = offset + sizeof(data); \
     }

#define DECODE(buf, size, offset, data)\
     if ((NULL == buf) || ( 0== size))\
     {\
     return -1;\
     }\
     if (offset + sizeof(data) > size)\
     {\
     return -1;\
     }\
     {\
     uint8_t *p = (uint8_t*)buf;\
     p=p+offset; \
     memcpy(&data,p,sizeof(data)); \
     offset = offset + sizeof(data); \
     }

#define ENCODE_STR(buf, size, offset, data, length)\
     if ((NULL == buf) || ( 0== size) || ( 0>= length) )\
     {\
     return -1;\
     }\
     if (offset + length > size)\
     {\
     return -1;\
     }\
     {\
     uint8_t *p = (uint8_t*)buf;\
     p=p+offset; \
     memcpy(p,data,length); \
     offset = offset+ length; \
     }

#define DECODE_STR(buf, size, offset, data, length)\
     if ((NULL == buf) || (0 == size) || (0 >= length) )\
     {\
     return -1;\
     }\
     if (offset + length > size)\
     {\
     return -1;\
     }\
     {\
     uint8_t *p = (uint8_t*)buf;\
     p=p+offset; \
     memcpy(data,p,length); \
     offset = offset+ length; \
     }

enum {
	enmMaxMsgLength = 1024,
	enmMaxNameLength = 20
};

//sha256哈希计算
string calculateHash(string message)
{
	SHA256 sha256;
	char* hashCode = new char[sha256.DigestSize()];
	sha256.CalculateDigest((byte*)hashCode, (const byte *)message.c_str(), message.size());
	static const char* const lut = "0123456789ABCDEF";
	size_t len = string(hashCode, sha256.DigestSize()).length();
	string hexHashCode;
	hexHashCode.reserve(2 * len);
	for (size_t i = 0; i < len; ++i)
	{
		const unsigned char c = string(hashCode, sha256.DigestSize())[i];
		hexHashCode.push_back(lut[c >> 4]);
		hexHashCode.push_back(lut[c & 15]);
	}
	return hexHashCode;
}

//ECDSA
AutoSeededRandomPool prng;
ECDSA<ECP, SHA256>::PrivateKey privateKey;
string sign(ECDSA<ECP, SHA256>::PrivateKey privateKey,string message)
{
	ECDSA<ECP, SHA256>::Signer signer(privateKey);
	size_t siglen = signer.MaxSignatureLength();
	string signature(siglen, 0x00);
	siglen = signer.SignMessage(prng, (const byte*)&message[0], message.size(), (byte*)&signature[0]);
	signature.resize(siglen);
	return signature;
}

bool verifySign(ECDSA<ECP, SHA256>::PrivateKey privateKey,string signature,string message)
{
	ECDSA<ECP, SHA256>::PublicKey publicKey;
	privateKey.MakePublicKey(publicKey);
	ECDSA<ECP, SHA256>::Verifier verifier(publicKey);
	bool result = verifier.VerifyMessage((const byte*)&message[0], message.size(), (const byte*)&signature[0], signature.size());
	return result;
}

//交易
class transaction{
public:
	int type;
	int nonce;
	int value;
	char receiveAddress[enmMaxNameLength];
	char sender[enmMaxNameLength];
	int gas;
	int data;
	int vrs;
	transaction()
	{

	}
	transaction(int type1, int nonce1, int value1, const char* address1,const char* sender1, int gas1, int data1, int vrs1) :type(type1), nonce(nonce1), value(value1), gas(gas1), data(data1), vrs(vrs1)
	{
		strcpy_s(receiveAddress, address1);
		strcpy_s(sender, sender1);
	}
	virtual ~transaction() {}
	virtual int encode(char *buf)
	{
		size_t offset = 0;
		memset(buf, '0', enmMaxMsgLength);
		ENCODE(buf, enmMaxMsgLength, offset, type);
		ENCODE(buf, enmMaxMsgLength, offset, nonce);
		ENCODE(buf, enmMaxMsgLength, offset, value);
		ENCODE_STR(buf, enmMaxMsgLength, offset, receiveAddress, enmMaxNameLength);
		ENCODE_STR(buf, enmMaxMsgLength, offset, sender, enmMaxNameLength);
		ENCODE(buf, enmMaxMsgLength, offset, gas);
		ENCODE(buf, enmMaxMsgLength, offset, data);
		ENCODE(buf, enmMaxMsgLength, offset, vrs);
		return offset;
	};
	virtual int decode(char *buf, size_t bufSize)
	{
		size_t offset = 0;
		DECODE(buf, bufSize, offset, type);
		DECODE(buf, bufSize, offset, nonce);
		DECODE(buf, bufSize, offset, value);
		DECODE_STR(buf, bufSize, offset, receiveAddress, enmMaxNameLength);
		DECODE_STR(buf, bufSize, offset, sender, enmMaxNameLength);
		DECODE(buf, bufSize, offset, gas);
		DECODE(buf, bufSize, offset, data);
		DECODE(buf, bufSize, offset, vrs);
		return offset;
	}
	void display()
	{
		cout << type << " " << nonce << " " << value << " " << receiveAddress<<" "<<sender << " " << gas << " " << data << " " << vrs << endl;
	}
};
//区块
class block {
	string parentHash;
	string transactionsRoot;
	string receiptsRoot;
	string logBloom;
	long number;
	time_t timestamp;
	long gasLimit;
	long gasUsed;
	char transactions[enmMaxMsgLength];
	size_t offset;
	string hash;
	block* next;
public:
	block(string parentHash1,long number1,time_t timestamp1,long gasLimit1,long gasUsed1,const char transactions1[enmMaxMsgLength],size_t offset1):gasLimit(gasLimit1),gasUsed(gasUsed1), number(number1)
	{
		parentHash=parentHash1;
		timestamp=timestamp1;
		offset = offset1;
		strcpy_s(transactions,transactions1);
		long temp_parent_hash = stol(parentHash,0,16);
		long temp_transactions = stol(transactions,0,2);
		hash = calculateHash(to_string(temp_parent_hash+number+timestamp+gasLimit+gasUsed+temp_transactions));
	}
	string get()
	{
		return transactions;
	}
	size_t getValue()
	{
		return offset;
	}
	string getHash()
	{
		return hash;
	}
	void update()
	{
		gasLimit -= gasUsed;
	}
	void updateNext(block* temp)
	{
		next = temp;
	}
};

//回执
class recipt {
	string blockHash;
	int blockNumber;
	string transactionHash;
	int transactionIndex;
	string from;
	string to;
	int cumulativeGasUsed;
	int gasUsed;
	string contractAddress;
	string logs;
public:
	recipt(string blockHash1,int blockNumber1,string transactionHash1,int transactionIndex1,string from1,string to1,int cumulativeGasUsed1,int gasUsed1,string contractAddress1,string logs1)
	{
		blockHash = blockHash1;
		blockNumber = blockNumber1;
		transactionHash=transactionHash1;
		transactionIndex=transactionIndex1;
		from=from1;
		to=to1;
		cumulativeGasUsed=cumulativeGasUsed1;
		gasUsed=gasUsed1;
		contractAddress=contractAddress1;
		logs=logs1;
	}
	void display()
	{
		cout<<"blockHash is "<< blockHash<<"\nblockNumber is "<<blockNumber<<"\ntransactionHash is "<<transactionHash<<"\ntransactionIndex is "<<transactionIndex<<"\nfrom is "<<from<<"\nto is"<<to<<"\ncumulativeGasUsed is "<<cumulativeGasUsed<<"\ngasUsed is "<<gasUsed<<"\ncontractAddress is "<<contractAddress<<"\nlogs is "<<logs<<endl;
	}
};